//===-- ABIMacOSX_i386.cpp --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "ABIMacOSX_i386.h"

// C Includes
// C++ Includes
#include <vector>

// Other libraries and framework includes
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"

// Project includes
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/RegisterValue.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Core/ValueObjectConstResult.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"

using namespace lldb;
using namespace lldb_private;

enum
{
    ehframe_eax = 0,
    ehframe_ecx,
    ehframe_edx,
    ehframe_ebx,
    ehframe_ebp,   // Different from DWARF the regnums - eh_frame esp/ebp had their regnums switched on i386 darwin
    ehframe_esp,   // Different from DWARF the regnums - eh_frame esp/ebp had their regnums switched on i386 darwin
    ehframe_esi,
    ehframe_edi,
    ehframe_eip,
    ehframe_eflags
};

enum
{
    dwarf_eax = 0,
    dwarf_ecx,
    dwarf_edx,
    dwarf_ebx,
    dwarf_esp,
    dwarf_ebp,
    dwarf_esi,
    dwarf_edi,
    dwarf_eip,
    dwarf_eflags,
    dwarf_stmm0 = 11,
    dwarf_stmm1,
    dwarf_stmm2,
    dwarf_stmm3,
    dwarf_stmm4,
    dwarf_stmm5,
    dwarf_stmm6,
    dwarf_stmm7,
    dwarf_xmm0 = 21,
    dwarf_xmm1,
    dwarf_xmm2,
    dwarf_xmm3,
    dwarf_xmm4,
    dwarf_xmm5,
    dwarf_xmm6,
    dwarf_xmm7,
    dwarf_ymm0 = dwarf_xmm0,
    dwarf_ymm1 = dwarf_xmm1,
    dwarf_ymm2 = dwarf_xmm2,
    dwarf_ymm3 = dwarf_xmm3,
    dwarf_ymm4 = dwarf_xmm4,
    dwarf_ymm5 = dwarf_xmm5,
    dwarf_ymm6 = dwarf_xmm6,
    dwarf_ymm7 = dwarf_xmm7
};

static RegisterInfo g_register_infos[] = 
{
  //  NAME      ALT      SZ OFF ENCODING         FORMAT                EH_FRAME              DWARF                 GENERIC                      PROCESS PLUGIN        LLDB NATIVE            VALUE REGS    INVALIDATE REGS
  //  ======    =======  == === =============    ============          ===================== ===================== ============================ ====================  ====================== ==========    ===============
    { "eax",    nullptr, 4,  0, eEncodingUint  , eFormatHex          , { ehframe_eax         , dwarf_eax           , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ebx"   , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { ehframe_ebx         , dwarf_ebx           , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ecx"   , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { ehframe_ecx         , dwarf_ecx           , LLDB_REGNUM_GENERIC_ARG4  , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "edx"   , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { ehframe_edx         , dwarf_edx           , LLDB_REGNUM_GENERIC_ARG3  , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "esi"   , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { ehframe_esi         , dwarf_esi           , LLDB_REGNUM_GENERIC_ARG2  , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "edi"   , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { ehframe_edi         , dwarf_edi           , LLDB_REGNUM_GENERIC_ARG1  , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ebp"   , "fp",    4,  0, eEncodingUint  , eFormatHex          , { ehframe_ebp         , dwarf_ebp           , LLDB_REGNUM_GENERIC_FP    , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "esp"   , "sp",    4,  0, eEncodingUint  , eFormatHex          , { ehframe_esp         , dwarf_esp           , LLDB_REGNUM_GENERIC_SP    , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "eip"   , "pc",    4,  0, eEncodingUint  , eFormatHex          , { ehframe_eip         , dwarf_eip           , LLDB_REGNUM_GENERIC_PC    , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "eflags", nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "cs"    , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ss"    , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ds"    , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "es"    , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "fs"    , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "gs"    , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm0" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm0         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm1" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm1         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm2" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm2         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm3" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm3         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm4" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm4         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm5" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm5         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm6" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm6         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "stmm7" , nullptr,10,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm7         , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "fctrl" , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "fstat" , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ftag"  , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "fiseg" , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "fioff" , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "foseg" , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "fooff" , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "fop"   , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm0"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm0          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm1"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm1          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm2"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm2          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm3"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm3          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm4"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm4          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm5"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm5          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm6"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm6          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "xmm7"  , nullptr,16,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm7          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "mxcsr" , nullptr, 4,  0, eEncodingUint  , eFormatHex          , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm0"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm0          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm1"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm1          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm2"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm2          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm3"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm3          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm4"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm4          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm5"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm5          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm6"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm6          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr },
    { "ymm7"  , nullptr,32,  0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm7          , LLDB_INVALID_REGNUM       , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM },      nullptr,           nullptr }
};

static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos);
static bool g_register_info_names_constified = false;

const lldb_private::RegisterInfo *
ABIMacOSX_i386::GetRegisterInfoArray (uint32_t &count)
{
    // Make the C-string names and alt_names for the register infos into const 
    // C-string values by having the ConstString unique the names in the global
    // constant C-string pool.
    if (!g_register_info_names_constified)
    {
        g_register_info_names_constified = true;
        for (uint32_t i = 0; i < k_num_register_infos; ++i)
        {
            if (g_register_infos[i].name)
                g_register_infos[i].name = ConstString(g_register_infos[i].name).GetCString();
            if (g_register_infos[i].alt_name)
                g_register_infos[i].alt_name = ConstString(g_register_infos[i].alt_name).GetCString();
        }
    }
    count = k_num_register_infos;
    return g_register_infos;
}

size_t
ABIMacOSX_i386::GetRedZoneSize () const
{
    return 0;
}

//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------

ABISP
ABIMacOSX_i386::CreateInstance (const ArchSpec &arch)
{
    static ABISP g_abi_sp;
     if ((arch.GetTriple().getArch() == llvm::Triple::x86) &&
          (arch.GetTriple().isMacOSX() || arch.GetTriple().isiOS() || arch.GetTriple().isWatchOS()))
     {
        if (!g_abi_sp)
            g_abi_sp.reset (new ABIMacOSX_i386);
        return g_abi_sp;
    }
    return ABISP();
}

bool
ABIMacOSX_i386::PrepareTrivialCall (Thread &thread, 
                                    addr_t sp, 
                                    addr_t func_addr, 
                                    addr_t return_addr, 
                                    llvm::ArrayRef<addr_t> args) const
{
    RegisterContext *reg_ctx = thread.GetRegisterContext().get();
    if (!reg_ctx)
        return false;    
    uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
    uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
    
    // When writing a register value down to memory, the register info used 
    // to write memory just needs to have the correct size of a 32 bit register, 
    // the actual register it pertains to is not important, just the size needs 
    // to be correct. Here we use "eax"...
    const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax");
    if (!reg_info_32)
        return false; // TODO this should actually never happen

    // Make room for the argument(s) on the stack

    Error error;
    RegisterValue reg_value;
    
    // Write any arguments onto the stack
    sp -= 4 * args.size();
    
    // Align the SP    
    sp &= ~(16ull-1ull); // 16-byte alignment
    
    addr_t arg_pos = sp;
    
    for (addr_t arg : args)
    {
        reg_value.SetUInt32(arg);
        error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
                                                     arg_pos,
                                                     reg_info_32->byte_size,
                                                     reg_value);
        if (error.Fail())
            return false;
        arg_pos += 4;
    }
    
    // The return address is pushed onto the stack (yes after we just set the
    // alignment above!).
    sp -= 4;
    reg_value.SetUInt32(return_addr);
    error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, 
                                                 sp, 
                                                 reg_info_32->byte_size, 
                                                 reg_value);
    if (error.Fail())
        return false;
    
    // %esp is set to the actual stack value.
    
    if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_num, sp))
        return false;
    
    // %eip is set to the address of the called function.
    
    if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_num, func_addr))
        return false;
    
    return true;
}

static bool 
ReadIntegerArgument (Scalar           &scalar,
                     unsigned int     bit_width,
                     bool             is_signed,
                     Process          *process,
                     addr_t           &current_stack_argument)
{
    
    uint32_t byte_size = (bit_width + (8-1))/8;
    Error error;
    if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, is_signed, scalar, error))
    {
        current_stack_argument += byte_size;
        return true;
    }
    return false;
}

bool
ABIMacOSX_i386::GetArgumentValues (Thread &thread,
                                   ValueList &values) const
{
    unsigned int num_values = values.GetSize();
    unsigned int value_index;
    
    // Get the pointer to the first stack argument so we have a place to start 
    // when reading data
    
    RegisterContext *reg_ctx = thread.GetRegisterContext().get();
    
    if (!reg_ctx)
        return false;
    
    addr_t sp = reg_ctx->GetSP(0);
    
    if (!sp)
        return false;
    
    addr_t current_stack_argument = sp + 4; // jump over return address
    
    for (value_index = 0;
         value_index < num_values;
         ++value_index)
    {
        Value *value = values.GetValueAtIndex(value_index);
        
        if (!value)
            return false;
        
        // We currently only support extracting values with Clang QualTypes.
        // Do we care about others?
        CompilerType compiler_type (value->GetCompilerType());
        if (compiler_type)
        {
            bool is_signed;
            
            if (compiler_type.IsIntegerType (is_signed))
            {
                ReadIntegerArgument(value->GetScalar(),
                                    compiler_type.GetBitSize(&thread),
                                    is_signed,
                                    thread.GetProcess().get(), 
                                    current_stack_argument);
            }
            else if (compiler_type.IsPointerType())
            {
                ReadIntegerArgument(value->GetScalar(),
                                    compiler_type.GetBitSize(&thread),
                                    false,
                                    thread.GetProcess().get(),
                                    current_stack_argument);
            }
        }
    }
    
    return true;
}

Error
ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp)
{
    Error error;
    if (!new_value_sp)
    {
        error.SetErrorString("Empty value object for return value.");
        return error;
    }
    
    CompilerType compiler_type = new_value_sp->GetCompilerType();
    if (!compiler_type)
    {
        error.SetErrorString ("Null clang type for return value.");
        return error;
    }
    
    Thread *thread = frame_sp->GetThread().get();
    
    bool is_signed;
    uint32_t count;
    bool is_complex;
    
    RegisterContext *reg_ctx = thread->GetRegisterContext().get();

    bool set_it_simple = false;
    if (compiler_type.IsIntegerType (is_signed) || compiler_type.IsPointerType())
    {
        DataExtractor data;
        Error data_error;
        size_t num_bytes = new_value_sp->GetData(data, data_error);
        if (data_error.Fail())
        {
            error.SetErrorStringWithFormat("Couldn't convert return value to raw data: %s", data_error.AsCString());
            return error;
        }
        lldb::offset_t offset = 0;
        if (num_bytes <= 8)
        {
            const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);
            if (num_bytes <= 4)
            {
                uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
        
                if (reg_ctx->WriteRegisterFromUnsigned (eax_info, raw_value))
                    set_it_simple = true;
            }
            else
            {
                uint32_t raw_value = data.GetMaxU32(&offset, 4);
        
                if (reg_ctx->WriteRegisterFromUnsigned (eax_info, raw_value))
                {
                    const RegisterInfo *edx_info = reg_ctx->GetRegisterInfoByName("edx", 0);
                    uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
                
                    if (reg_ctx->WriteRegisterFromUnsigned (edx_info, raw_value))
                        set_it_simple = true;
                }
            }
        }
        else
        {
            error.SetErrorString("We don't support returning longer than 64 bit integer values at present.");
        }
    }
    else if (compiler_type.IsFloatingPointType (count, is_complex))
    {
        if (is_complex)
            error.SetErrorString ("We don't support returning complex values at present");
        else
            error.SetErrorString ("We don't support returning float values at present");
    }
    
    if (!set_it_simple)
        error.SetErrorString ("We only support setting simple integer return types at present.");
    
    return error;
}

ValueObjectSP
ABIMacOSX_i386::GetReturnValueObjectImpl (Thread &thread,
                                          CompilerType &compiler_type) const
{
    Value value;
    ValueObjectSP return_valobj_sp;
    
    if (!compiler_type)
        return return_valobj_sp;
    
    //value.SetContext (Value::eContextTypeClangType, compiler_type.GetOpaqueQualType());
    value.SetCompilerType (compiler_type);
    
    RegisterContext *reg_ctx = thread.GetRegisterContext().get();
        if (!reg_ctx)
        return return_valobj_sp;
        
    bool is_signed;
            
    if (compiler_type.IsIntegerType (is_signed))
    {
        size_t bit_width = compiler_type.GetBitSize(&thread);
        
        unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
        unsigned edx_id = reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB];
        
        switch (bit_width)
        {
            default:
            case 128:
                // Scalar can't hold 128-bit literals, so we don't handle this
                return return_valobj_sp;
            case 64:
                uint64_t raw_value;
                raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff;
                raw_value |= (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) & 0xffffffff) << 32;
                if (is_signed)
                    value.GetScalar() = (int64_t)raw_value;
                else
                    value.GetScalar() = (uint64_t)raw_value;
                break;
            case 32:
                if (is_signed)
                    value.GetScalar() = (int32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff);
                else
                    value.GetScalar() = (uint32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff);
                break;
            case 16:
                if (is_signed)
                    value.GetScalar() = (int16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff);
                else
                    value.GetScalar() = (uint16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff);
                break;
            case 8:
                if (is_signed)
                    value.GetScalar() = (int8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff);
                else
                    value.GetScalar() = (uint8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff);
                break;
        }
    }
    else if (compiler_type.IsPointerType ())
    {
        unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
        uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff;
        value.GetScalar() = ptr;
    }
    else
    {
        // not handled yet
        return return_valobj_sp;
    }
    
    // If we get here, we have a valid Value, so make our ValueObject out of it:
    
    return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(),
                                                      value,
                                                      ConstString(""));
    return return_valobj_sp;
}

// This defines the CFA as esp+4
// the saved pc is at CFA-4 (i.e. esp+0)
// The saved esp is CFA+0

bool
ABIMacOSX_i386::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan)
{
    unwind_plan.Clear();
    unwind_plan.SetRegisterKind (eRegisterKindDWARF);

    uint32_t sp_reg_num = dwarf_esp;
    uint32_t pc_reg_num = dwarf_eip;
    
    UnwindPlan::RowSP row(new UnwindPlan::Row);
    row->GetCFAValue().SetIsRegisterPlusOffset (sp_reg_num, 4);
    row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false);
    row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
    unwind_plan.AppendRow (row);
    unwind_plan.SetSourceName ("i386 at-func-entry default");
    unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
    return true;
}

// This defines the CFA as ebp+8
// The saved pc is at CFA-4 (i.e. ebp+4)
// The saved ebp is at CFA-8 (i.e. ebp+0)
// The saved esp is CFA+0

bool
ABIMacOSX_i386::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan)
{
    unwind_plan.Clear ();
    unwind_plan.SetRegisterKind (eRegisterKindDWARF);

    uint32_t fp_reg_num = dwarf_ebp;
    uint32_t sp_reg_num = dwarf_esp;
    uint32_t pc_reg_num = dwarf_eip;
    
    UnwindPlan::RowSP row(new UnwindPlan::Row);
    const int32_t ptr_size = 4;

    row->GetCFAValue().SetIsRegisterPlusOffset (fp_reg_num, 2 * ptr_size);
    row->SetOffset (0);
    
    row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
    row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
    row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);

    unwind_plan.AppendRow (row);
    unwind_plan.SetSourceName ("i386 default unwind plan");
    unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
    unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
    return true;
}

bool
ABIMacOSX_i386::RegisterIsVolatile (const RegisterInfo *reg_info)
{
    return !RegisterIsCalleeSaved (reg_info);
}

// v. http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4
//
// This document ("OS X ABI Function Call Guide", chapter "IA-32 Function Calling Conventions")
// says that the following registers on i386 are preserved aka non-volatile aka callee-saved:
// 
// ebx, ebp, esi, edi, esp

bool
ABIMacOSX_i386::RegisterIsCalleeSaved (const RegisterInfo *reg_info)
{
    if (reg_info)
    {
        // Saved registers are ebx, ebp, esi, edi, esp, eip
        const char *name = reg_info->name;
        if (name[0] == 'e')
        {
            switch (name[1])
            {
            case 'b': 
                if (name[2] == 'x' || name[2] == 'p')
                    return name[3] == '\0';
                break;
            case 'd':
                if (name[2] == 'i')
                    return name[3] == '\0';
                break;
            case 'i': 
                if (name[2] == 'p')
                    return name[3] == '\0';
                break;
            case 's':
                if (name[2] == 'i' || name[2] == 'p')
                    return name[3] == '\0';
                break;
            }
        }
        if (name[0] == 's' && name[1] == 'p' && name[2] == '\0')   // sp
            return true;
        if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0')   // fp
            return true;
        if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0')   // pc
            return true;
    }
    return false;
}

void
ABIMacOSX_i386::Initialize()
{
    PluginManager::RegisterPlugin (GetPluginNameStatic(),
                                   "Mac OS X ABI for i386 targets",
                                   CreateInstance);    
}

void
ABIMacOSX_i386::Terminate()
{
    PluginManager::UnregisterPlugin (CreateInstance);
}

lldb_private::ConstString
ABIMacOSX_i386::GetPluginNameStatic ()
{
    static ConstString g_short_name("abi.macosx-i386");
    return g_short_name;
    
}

//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------

lldb_private::ConstString
ABIMacOSX_i386::GetPluginName()
{
    return GetPluginNameStatic();
}

uint32_t
ABIMacOSX_i386::GetPluginVersion()
{
    return 1;
}
